﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.OpenGL;
using System.Drawing;
using Silvermoon.Shapes;
using Silvermoon.Windows;
using Silvermoon.UI;
using Silvermoon.Classes;

namespace Silvermoon.Controls
{
    /// <summary>
    ///  Item with name and value property, which implements automatic height depending on the text for the value property.
    /// </summary>
    public class NameValueItem : VirtualItem
    {
        // remark: InvalidationFlag.Internal is used to mark the item to recalculate it's height on PreLayout.
        //              InvalidationFlag.Internal2 is used to notify the listview about the modified size as soon as it is attached.

        #region fields

        const int NAMEFONT_NOT_NULL = 8;
        const int VALUEFONT_NOT_NULL = 16;

        private StringShape nameLabel;
        private StringShape valueLabel;
        private Margin padding = new Margin(8);

        #endregion
        #region accessors

        public static PropertyAccessor<NameValueItem, Color> NameColorProperty = PropertyAccessor.Register<NameValueItem, Color>("NameColor",
            c => c.NameColor,
            (c, v) => c.NameColor = v);

        #endregion

        #region ctor
        public NameValueItem()
            : base()
        {
            AutoHeight = true;
            nameLabel = new StringShape
            {
                LineAlignment = Alignment.Near,
                Alignment = Alignment.Near,
                LineMode = LineMode.EndEllipsis,
                Color = PhoneColors.Accent,
                FontScale = 0.75f,
            };
            valueLabel = new StringShape
            {
                LineMode = LineMode.WordWrap,
                LineAlignment = Alignment.Near,
                Alignment = Alignment.Near,
                Color = Color.Empty,
            };
            Shapes.AddRange(nameLabel, valueLabel);
        }


        #endregion
        #region props

        public Margin Padding
        {
            get { return padding ?? Margin.Zero; }
            set
            {
                if (!Padding.Equals(value))
                {
                    padding = value;
                    Invalidate(InvalidationFlag.Layout | InvalidationFlag.Internal);
                }
            }
        }

        public FontSprite NameFont
        {
            get { return nameLabel.Font; }
            set
            {
                bitmask |= NAMEFONT_NOT_NULL;
                if (nameLabel.Font != value)
                {
                    nameLabel.Font = value;
                    Invalidate(InvalidationFlag.Layout | InvalidationFlag.Internal);
                }
            }
        }

        public FontSprite ValueFont
        {
            get { return valueLabel.Font; }
            set
            {
                bitmask |= NAMEFONT_NOT_NULL;
                if (valueLabel.Font != value)
                {
                    valueLabel.Font = value;
                    Invalidate(InvalidationFlag.Layout | InvalidationFlag.Internal);
                }
            }
        }

        public Color NameColor
        {
            get { return nameLabel.Color; }
            set
            {
                if (nameLabel.Color != value)
                {
                    nameLabel.Color = value;
                    Invalidate(InvalidationFlag.Color);
                }
            }
        }

        public float NameFontScale
        {
            get { return nameLabel.FontScale; }
            set
            {
                if (NameFontScale != value)
                {
                    nameLabel.FontScale = value;
                    Invalidate(InvalidationFlag.Layout | InvalidationFlag.Internal);
                }
            }
        }


        /// <summary>
        /// Gets or sets the alignment for name and value.
        /// </summary>
        public Alignment Alignment
        {
            get { return nameLabel.Alignment; }
            set
            {
                nameLabel.Alignment = value;
                valueLabel.Alignment = value;
            }
        }

        /// <summary>
        /// Gets or sets the text for the name. which is displayed as single line.
        /// </summary>
        public string Name
        {
            get { return nameLabel.Text; }
            set
            {
                if (nameLabel.Text != value)
                {
                    nameLabel.Text = value;
                    OnNameChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the text for the value which is displayed wordwrapped in multiple lines.
        /// </summary>
        public string Value
        {
            get { return valueLabel.Text; }
            set
            {
                if (Value != value)
                {
                    valueLabel.Text = value;
                    OnValueChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets whether the item automatically determins its height depending on the name and value.
        /// </summary>
        public bool AutoHeight { get; set; }

        #endregion
        #region methods

        protected virtual void OnNameChanged()
        {
            Invalidate(InvalidationFlag.Layout | InvalidationFlag.Text | InvalidationFlag.Internal);
        }

        protected virtual void OnValueChanged()
        {
            Invalidate(InvalidationFlag.Layout | InvalidationFlag.Text | InvalidationFlag.Internal);
        }

        protected override void OnSizeChanging(Size oldValue)
        {
            if (AutoHeight && oldValue.Width != Width)
            {
                DetermineHeight();
            }
            base.OnSizeChanging(oldValue);
        }

        protected override void OnMarginChanged()
        {
            base.OnMarginChanged();
            Invalidate(InvalidationFlag.Internal);
        }


        private void DetermineHeight()
        {
            if (AutoHeight && Width > 0 && Host != null && Host.IsDynamicItemHeightSupported)
            {
                IItemHost listView = Host;
                if (listView != null)
                {
                    Size size = new Size(Width - padding.Width, 8192);
                    FontSprite font = valueLabel.Font;
                    var strings = font.WordWrapLines(valueLabel.Text, size, 0);
                    int h = (strings.Count) * font.CharHeight;
                    Height = h + nameLabel.Font.CharHeight + padding.Height;
                    if (listView != null) listView.SetItemHeight(this.ItemIndex, Height + Margin.Height);
                    else Invalidate(InvalidationFlag.Internal2);
                }
                else Invalidate(InvalidationFlag.Internal);
            }
        }

        protected override void OnHostChanged()
        {
            base.OnHostChanged();
            if (Host != null)
            {
                if (CheckFlag(InvalidationFlag.Internal2)) Host.SetItemHeight(this.ItemIndex, Height + Margin.Height);
            }
            else
            {
                InvalidationMask &= ~InvalidationFlag.Internal2;
            }
        }


        protected override void PreLayout()
        {
            if (CheckFlag(InvalidationFlag.Internal))
            {
                DetermineHeight();
            }
            base.PreLayout();
        }

        protected override void OnLayout()
        {
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Layout))
            {
                Rectangle bounds = new Rectangle(padding.LeftZero, padding.TopZero, Width - padding.Width, nameLabel.Font.CharHeight);
                nameLabel.Bounds = bounds;

                bounds.Y = bounds.Bottom;
                bounds.Height = Height - bounds.Top;
                valueLabel.Bounds = bounds;
            }
            base.OnLayout();
        }

        protected override void ApplyStyle()
        {
            base.ApplyStyle();
            if ((bitmask & NAMEFONT_NOT_NULL) == 0) NameFont = SystemFonts.Normal;
            if ((bitmask & VALUEFONT_NOT_NULL) == 0) ValueFont = SystemFonts.Medium;
        }


        #endregion
    }
}
